<template>
  <div class="pay-col-sch-info">
    <!--    自定义数据表格-->
    <el-form ref="customForm" class="custom-form" :model="this" size="mini">
      <el-table :data="tableData" empty-text="请先将回款规则填写完整">
        <el-table-column label="序号" type="index" :index="1" align="center" width="120px"></el-table-column>
        <el-table-column label="回款日期" align="center">
          <template v-slot="{row,$index}">
            <template v-if="method === 0">
              <el-form-item :prop="`tableData[${$index}].collectionDate`"
                            :rules="{required:true,message:'请输入回款日期',trigger:'change'}">
                <el-date-picker v-model="tableData[$index].collectionDate" type="date" placeholder="请选择"
                                clearable/>
              </el-form-item>
            </template>
            <template v-else>
              <div>{{ row.collectionDate }}</div>
            </template>
          </template>
        </el-table-column>
        <el-table-column label="回款计划名称" align="center">
          <template v-slot="{row,$index}">
            <el-form-item :prop="`tableData[${$index}].scheduleName`"
                          :rules="{required:true,validator:checkScheduleName,trigger:'change'}">
              <el-input v-model.trim="tableData[$index].scheduleName" placeholder="请输入" maxlength="30"
                        clearable/>
            </el-form-item>
          </template>
        </el-table-column>
        <el-table-column align="center">
          <template #header>
            <el-dropdown @command="handleCommand">
              <div class="el-dropdown-link">
                <span>{{ amountMethodLabel }}</span><i class="el-icon-arrow-down el-icon--right"></i>
              </div>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item :command="0">金额(元)</el-dropdown-item>
                  <el-dropdown-item :command="1">占比(%)</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </template>
          <template v-slot="{row,$index}">
            <!--            计算规则 - 自定义-->
            <template v-if="rule === 1">
              <el-form-item :prop="`tableData[${$index}].amountValue`"
                            :rules="[
                              {required:true,message:'请输入金额/占比',trigger:'blur'},
                              {validator:checkAmountValue,trigger: 'blur'}
                          ]">
                <el-input v-model.trim="tableData[$index].amountValue" :placeholder="remainingAmount"
                          clearable></el-input>
              </el-form-item>
            </template>
            <!--            计算规则 - 均分-->
            <template v-else>
              <div>{{ main.amountReceivable ? row.amountValue : '请先填写应收金额' }}</div>
            </template>
          </template>
        </el-table-column>
        <el-table-column v-if="method === 0" label="操作" align="center">
          <template v-slot="{row,$index}">
            <el-button type="text" @click="handleAddColumn($index)">新增</el-button>
            <template v-if="$index>0">
              <el-divider direction="vertical"></el-divider>
              <el-button type="text" @click="handleRemoveColumn($index)">删除</el-button>
            </template>
          </template>
        </el-table-column>
      </el-table>
    </el-form>
  </div>
</template>

<script>
/**
 * 回款计划表格数据:{
 *   @param amountMethod [number]      // 选择金额方式（0-金额(元) / 1-占比）
 *   @param amountValue [number]       // 值(金额/占比)
 *   @param collectionDate [string]    // 回款日期
 *   @param orderNum [number]          // 序号
 *   @param scheduleName [string]      // 回款计划名称
 * }
 */

import {debounce, min} from 'lodash'
import currency from 'currency.js'
import dayjs from '@/plugins/dayjs'
import {formatDateByQuarter} from '../common/dict'

export default {
  name: "PayColSchInfo",
  props: {
    // 回款方式（0-自定义 1-月度 2-季度 3-年度）
    method: {
      required: true,
      type: Number
    },
    // 计算规则（1-自定义 2-均分）
    rule: {
      required: true,
      type: Number
    },
    // 表格总列数，对应 “总 月\季\年 数”
    columns: Number,
    // 回款方式表单
    info: {
      required: true,
      type: Object
    },
    // 回款基本信息表单
    main: {
      required: true,
      type: Object
    }
  },
  // 接收注入的addPlan组件的实例对象
  inject: ['vm'],
  data() {
    return {
      amountMethod: 0,
      tableData: [{
        collectionDate: '',
        scheduleName: '',
        amountValue: ''
      }],
      // 维护一个状态，用来特殊处理提交表单时的验证
      isSubmit: false,
    }
  },
  computed: {
    amountMethodLabel() {
      return this.amountMethod === 1 ? '占比(%)' : '金额(元)'
    },

    // 计算剩余应收金额
    remainingAmount() {
      if (this.main.amountReceivable) {
        // 计算当前表格项所有输入值的总和  （可以是金钱总和也可以是占比总和）
        const prevTotal = this.tableData.reduce((prev, curr) => prev.add(curr.amountValue), currency(0))?.value
        if (this.amountMethod === 0) {
          // 如果是 金额模式
          // 计算当前输入总额与应收金额的差值
          const amount = currency(this.main.amountReceivable).subtract(prevTotal).value
          return `剩余应收金额：${amount < 0 ? 0 : amount}`
        } else {
          // 如果是 占比模式
          // 计算当前占比与100%的差值
          const proportion = currency(100).subtract(prevTotal).value
          return `剩余占比：${proportion < 0 ? 0 : proportion}%`
        }
      } else {
        return '请输入'
      }
    }
  },
  watch: {
    // 监听回款方式的变化，重置表单数据
    method() {
      this.resetTableData()
    },

    // 监听addPlan组件 应收金额的变化
    'main.amountReceivable': {
      handler(n) {
        if (n) {
          this.onAmountReceivableChange(n)
        }
      }
    },

    // 监听计算规则的变化
    rule(n) {
      if (n === 1) {
        // 如果切换回了自定义模式，重置 金额/占比 内容
        this.resetTableData('money')
      } else if (n === 2) {
        // 如果切换到均分模式，判断应收金额是否填写，若未填写则显示报错
        if (!this.main.amountReceivable) {
          this.vm.$refs.basicForm.validateField('amountReceivable')
        }
      }
    },

    // 监听回款规则表单是否填写完整
    info: {
      handler(n) {
        // 自定义模式下不执行以下逻辑
        if (this.method === 0) return
        if (n instanceof Object) {
          // 如果找到了对象的值中存在 “空值” 证明回款规则表单没有全部填写完全
          const flag = Object.values(n)?.some(i => {
            if (i instanceof Array) {
              // 值可能为数组， 尝试寻找数组中是否存在空值或者数组length是否为0
              return i.some(j => !j) || !i.length  // !i.length 当数组长度为0时返回true，意味着Object.values(n)?.some找到了“空值”
            } else {
              // 判断其他值是否为空值
              return !i
            }
          })
          if (!flag) {
            // 如果填写完整，则生成对应表格内容
            this.generateTableData()
            // 处理回款日期
            this.formatCollectionDate(n)
            // 处理均分数据
            this.maybeIsEqual(this.main.amountReceivable, this.columns)
          }
        }
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    // 表头下拉菜单变化时的回调
    handleCommand(command) {
      if (command === this.amountMethod) return
      // 如果是均分模式，直接切换
      if (this.rule === 2) {
        this.amountMethod = command
        this.maybeIsEqual()
        return;
      }
      this.$confirm('切换模式将会清空已填写的金额/占比内容，是否确定切换?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.amountMethod = command
        this.resetTableData('money')
      })
    },

    // addPlan组件 应收金额的变化的回调
    onAmountReceivableChange: debounce(function (n) {
      if (n) {
        this.maybeIsEqual(n, this.columns)
      }
    }, 500),

    // 重置表格内容
    resetTableData(mode = "all") {
      if (mode === 'all') {
        // 如果是all模式，则初始化表格
        if (this.method === 0) {
          // 如果是自定义，默认填充一个数据（方便新增）
          this.$set(this, 'tableData', [{
            collectionDate: '',
            scheduleName: '',
            amountValue: ''
          }])
        } else {
          // 如果不是自定义模式，直接填充空数组
          this.$set(this, 'tableData', [])
        }
      } else if (mode === 'money') {
        // 如果是money模式，仅重置 金额/占比 字段
        this.tableData.forEach(i => i.amountValue = '')
      }

      // 如果是自定义表格，初始后清空验证
      if (this.method === 0) {
        this.$refs.customForm?.clearValidate()
      }
    },

    // 触发表单验证
    validateForm() {
      this.isSubmit = true
      return new Promise((resolve, reject) => {
        this.$refs.customForm.validate((valid) => {
          if (valid) {
            const newTableData = this.tableData.map((i, idx) => {
              return {
                ...i,
                amountMethod: this.amountMethod,
                orderNum: idx + 1
              }
            })
            resolve(newTableData)
          } else {
            reject()
          }
        })
      })
    },

    // ------回款方式：自定义模式---------------------------------------------
    // 新增列
    handleAddColumn(index) {
      this.$refs.customForm.validate((valid) => {
        // 表单验证通过或者此时处于提交模式的特殊验证规则下，都允许新建表格列
        if (valid || this.isSubmit) {
          this.tableData.splice(index + 1, 0, {
            collectionDate: '',
            scheduleName: '',
            amountValue: ''
          })
          this.$nextTick(() => {
            this.$refs.customForm.clearValidate()
          })
        }
      })
    },
    // 删除列
    handleRemoveColumn(index) {
      this.tableData.splice(index, 1)
    },
    // 验证回款计划名称输入框
    checkScheduleName(rule, value, callback) {
      if (value) {
        // 判断名称是否已有重名项
        const result = this.tableData.filter(i => i.scheduleName === value)
        if (result.length > 1) {
          callback(new Error('回款计划名称重复'))
        } else {
          callback()
        }
      } else {
        callback(new Error('请输入回款计划名称'))
      }
    },
    // 验证金额\占比
    checkAmountValue(rule, value, callback) {
      // 如果应收金额为空，先报错提示
      if (!this.main.amountReceivable) {
        callback(new Error('请先输入应收金额'))
        // 触发addPlan组价 应收金额的表单验证
        this.vm.$refs.basicForm.validateField('amountReceivable')
        return
      }

      // 定义验证金钱/占比的正则
      const moneyRegExp = /(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0){1}$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/
      // 计算当前表格项所有输入值的总和
      const prevTotal = this.tableData.reduce((prev, curr) => prev.add(curr.amountValue), currency(0))?.value
      // 获取应收金额的值
      const amountReceivable = currency(this.main.amountReceivable).value

      // 如果输入的不是正确金额/占比，报错
      if (!moneyRegExp.test(value)) return callback(new Error('请输入正确的金额/占比'))

      // 金额模式，如果超过应收金额，报错
      if (this.amountMethod === 0 && prevTotal > amountReceivable) {
        return callback(new Error('总金额数已超过应收金额，请核对'))
      }

      // 占比模式，如果超过100%，报错
      if (this.amountMethod === 1 && prevTotal > 100) {
        return callback(new Error('总占比已超过100%，请核对'))
      }

      // 维护一个提交时才触发的验证
      if (this.isSubmit) {
        if (this.amountMethod === 0) {
          // 金额模式 已填写的金额总值需要等于应收金额
          if (prevTotal !== amountReceivable) {
            callback(new Error('已填写的金额总值不等于应收金额，请核对'))
          } else {
            // 若验证通过 则关闭此处的验证
            this.isSubmit = false
            // 再次验证，清空此验证的所有报错提示
            this.$refs.customForm.validate()
          }
        } else if(this.amountMethod === 1) {
          // 占比模式 已填写的占比总和需等于100%
          if (prevTotal !== 100) {
            callback(new Error('已填写的总占比值不等于100%，请核对'))
          } else {
            this.isSubmit = false
            this.$refs.customForm.validate()
          }
        }
      }

      callback()
    },


    // ------回款方式：非自定义模式---------------------------------------------
    // 生成表格数据
    generateTableData() {
      // 获取当前tableData的length
      const currLength = this.tableData.length
      // 计算差值
      const difference = this.columns - currLength
      if (difference < 0) {
        // 删除后面几项
        this.$set(this, 'tableData', this.tableData.slice(0, difference))
      } else {
        // 追加差值数据
        let newData = new Array(difference).fill(null)
        newData = newData.map(i => ({
          collectionDate: '',
          scheduleName: '',
          amountValue: ''
        }))
        this.$set(this, 'tableData', this.tableData.concat(newData))
      }
    },
    // 处理回款日期
    formatCollectionDate(info = this.info) {
      switch (this.method) {
        case 1:   // 月
          const monthFirst = dayjs(info.monthFirst, 'YYYY-MM')  // 依据年月创建dayjs实例
          this.tableData.forEach((i, idx) => {
            const targetMonth = monthFirst.add(idx, 'month') // 第index月后的dayjs实例
            const targetMonthLastDay = targetMonth.endOf('month').date() // 当前月最后一天
            const minValue = min([targetMonthLastDay, info.monthCollectionDay]) // 比较最小值
            i.collectionDate = targetMonth.date(minValue).format('YYYY-MM-DD')
          })
          return
        case 2:   // 季
          const quarterFirst = dayjs(formatDateByQuarter(info?.quarterFirst[0], info?.quarterFirst[1], info?.quarterCollectionDay[0]), 'YYYY-M')
          this.tableData.forEach((i, idx) => {
            const targetQuarter = quarterFirst.add(idx, 'quarter') // 第index季度后的dayjs实例
            const targetQuarterMonthLastDay = targetQuarter.endOf('month').date() // 当前月最后一天
            const minValue = min([targetQuarterMonthLastDay, info.quarterCollectionDay[1]]) // 比较最小值
            i.collectionDate = targetQuarter.date(minValue).format('YYYY-MM-DD')
          })
          return;
        case 3:   // 年
          const yearFirstStr = info.yearFirst + '-' + info.yearCollectionDay[0] + '-' + info.yearCollectionDay[1]   // 将数据处理成 'YYYY-M-D' 的字符串形式
          const yearFirst = dayjs(yearFirstStr, 'YYYY-M-D')
          this.tableData.forEach((i, idx) => {
            const targetYear = yearFirst.add(idx, 'year') // 第index季度后的dayjs实例
            i.collectionDate = targetYear.format('YYYY-MM-DD')
          })
          return;
        default:
          return;
      }
    },

    // 如果是均分模式，处理每个项的金额\占比数据
    maybeIsEqual(sum = this.main.amountReceivable, columns = this.columns) {
      // 如果是均分模式，再进行均分
      if (this.rule === 2) {
        // 如果是均分模式，那sum的值应为100 （100%）
        if (this.amountMethod === 1) sum = 100

        // 如果总金额数小于1.2,会存在均分不成功的情况，需特殊处理
        if (sum > 1.2) {
          const singleItem = currency(sum).divide(columns).value
          const lastItem = currency(sum).subtract(currency(singleItem).multiply(columns - 1).value).value
          this.tableData.forEach((i, idx, target) => {
            i.amountValue = idx === target.length - 1 ? lastItem : singleItem
          })
        } else {
          // 利用currency的均分模式，再通过排序把0放到末尾，其余值就按从小到大顺序
          const arr = currency(sum).distribute(columns).map(i => i.value).sort((i, j) => i === 0 ? 0 : i - j)
          this.tableData.forEach((i, idx) => {
            i.amountValue = arr[idx]
          })
        }
      }
    }
  }
}
</script>

<style scoped lang="scss">
.pay-col-sch-info {
  ::v-deep {
    .el-dropdown-link {
      color: inherit;
      font-size: 13px;
    }

    .el-table__cell {
      padding-bottom: 10px !important;

      .el-form-item {
        margin-top: 12px;
      }
    }
  }


  .custom-form ::v-deep {
    .el-input__inner {
      text-align: center !important;
    }
  }
}
</style>
